package emulator.small;

import java.util.Observer;

import emulator.hardware.bits.SharedBit;
import emulator.hardware.bus.Bus;
import emulator.hardware.clock.Clock;
import emulator.hardware.clock.ClockHandle;
import emulator.hardware.clock.TickScales;
import emulator.hardware.debug.BusWatchException;
import emulator.hardware.memory.MemoryBlock;
import emulator.hardware.memory.UnmappedMemoryException;
import emulator.hardware.video.test.NullClock;

public class Board {
	MemoryBlock memory;
	TickScales clock_scales = new TickScales();
	ViaStub via1 = new ViaStub();
	ViaStub via2 = new ViaStub();

	public void setIrqLines(SharedBit nmi_out, SharedBit irq_out)
	{
		via1.setIrqOut(nmi_out);
		via2.setIrqOut(irq_out);
	}
	
	public MemoryBlock getMemory() {
		return memory;
	}

	public void setMemory(MemoryBlock memory) {
		this.memory = memory;
	}


	public void handleClock(long tick_cnt) {
		via1.handleClock(tick_cnt);
		via2.handleClock(tick_cnt);
		
	}

	public Bus getBus() {
		return new BoardBus();
	}
	
	public Clock getCpuClock() {
		return new CpuClock();
	}
	
	public Clock getVicClock() {
		return new NullClock();
	}
	
	public class BoardBus implements Bus {

		@Override
		public void write(int address, int data) throws BusWatchException,
				UnmappedMemoryException {
			if (address < 0xC000)
			{
				if (address >= 0x2000 && address < 0x9000)
				{
					// expansion area & char ROM
				}
				else if (address >= 0x9000 && address < 0x9400)
				{ // I/O 00
					if (address <= 0x900f)
					{
						// VIC
					}
					else // if (0x9100 <= n_address)
					{
						if ((address & 0x10) != 0)
							via1.writeRegister(address, data);
						else if ((address & 0x20) != 0)
							via2.writeRegister(address, data);
					}
				}
				else
					memory.write(address, data);
			}
		}

		@Override
		public int read(int address) throws BusWatchException,
				UnmappedMemoryException {
			if (address >= 0x9000 && address < 0x9400)
			{ // I/O 00
				if (address <= 0x900f)
				{
					// VIC
				}
				else // if (0x9100 <= n_address)
				{
					if ((address & 0x10) != 0)
						return via1.readRegister(address);
					else if ((address & 0x20) != 0)
						return via2.readRegister(address);
				}
			}
			else if (address < 0x0400 ||
				(address >= 0x1000 && address < 0x2000) ||
				address >= 0x8000)
				return memory.read(address);
			// unconnected area
			return (address >> 8) & 0xff;
		}
	}
	
	public class CpuClock implements Clock {
		@Override
		public ClockHandle acquireHandle() {
			return new CpuClockHandle();
		}
	
		@Override
		public void releaseHandle(ClockHandle handle) {
		}
	
		@Override
		public void addClockSpeedObserver(Observer clockSpeedObserver) {
			clock_scales.addObserver(clockSpeedObserver);
		}
	}
	
	public class CpuClockHandle implements ClockHandle {
		
		long tick_cnt = 0;
		
		CpuClockHandle()
		{
			clock_scales.init();
		}
		
		@Override
		public void tick() {
			tick(1);
		}

		@Override
		public void tick(int x) {
			tick_cnt += x;
			clock_scales.measureTicks(tick_cnt);
			handleClock(tick_cnt);
		}

		@Override
		public long getTicks() {
			return tick_cnt;
		}
	}
}
